package com.ruoyi.util;

import com.ruoyi.vo.ProcessDefinitionVo;
import com.ruoyi.vo.ProcessInstanceVo;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricProcessInstanceQuery;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.activiti.spring.SpringProcessEngineConfiguration;

import java.io.*;
import java.util.*;


public class ActivitiUtil {
    private static final String DEVICE = "approvalResult";
    private static final String APPROVED = "approved";
    private static final String REJECTED = "rejected";


    /**
     * 根据文件绝对路径部署 BPMN 文件并指定部署名称
     *
     * @param bpmnAbsoluteFilePath 文件绝对路径
     * @param deploymentName       部署名称
     */
    public static Deployment deployProcessDefinitionByAbsolutePath(String bpmnAbsoluteFilePath, String deploymentName) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();

        File file = new File(bpmnAbsoluteFilePath);

        try (InputStream inputStream = new FileInputStream(file)) {
            String resourceName = file.getName();
            Deployment deployment = repositoryService.createDeployment()
                    .name(deploymentName)
                    .addInputStream(resourceName, inputStream)
                    .deploy();

            return deployment;
        } catch (IOException e) {
            throw new RuntimeException("部署错误");
        }
    }

    /**
     * 通过流程定义 ID 启动新的流程实例并关联业务 ID
     *
     * @param processDefinitionId 流程定义 ID
     * @param businessId          业务 ID
     * @param variables           流程变量
     * @return 启动的流程实例
     */
    public static ProcessInstance startProcessInstanceById(String processDefinitionId, String businessId, Map<String, Object> variables) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();

        if (variables == null) {
            return runtimeService.startProcessInstanceById(processDefinitionId, businessId);
        } else {
            return runtimeService.startProcessInstanceById(processDefinitionId, businessId, variables);
        }
    }

    /**
     * 根据流程实例 ID 获取流程变量
     *
     * @param processInstanceId 流程实例 ID
     * @return 流程变量的 Map，如果流程实例不存在则返回 null
     */
    public static Map<String, Object> getProcessVariablesByProcessInstanceId(String processInstanceId) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();

        if (processInstance != null) {
            return runtimeService.getVariables(processInstance.getId());
        }
        return null;
    }

    /**
     * 根据流程定义Key返回部署的流程定义信息列表
     *
     * @param processDefinitionKey 流程定义Key
     * @return 部署的流程定义信息列表
     */
    public static List<ProcessDefinitionVo> getProcessDefinitionIdsByKey(String processDefinitionKey) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();

        List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(processDefinitionKey)
                .orderByProcessDefinitionVersion()
                .desc()
                .list();

        List<ProcessDefinitionVo> processDefinitionInfos = new ArrayList<>();
        for (ProcessDefinition processDefinition : processDefinitions) {
            String deploymentId = processDefinition.getDeploymentId();
            Deployment deployment = repositoryService.createDeploymentQuery()
                    .deploymentId(deploymentId)
                    .singleResult();
            String deploymentName = deployment.getName();
            ProcessDefinitionVo processDefinitionVo = new ProcessDefinitionVo(
                    processDefinition.getId(),
                    deployment.getName(),
                    deployment.getDeploymentTime()
            );
            processDefinitionInfos.add(processDefinitionVo);
        }
        return processDefinitionInfos;
    }

    /**
     * 根据流程定义 ID 查询所有流程实例信息列表
     *
     * @param processDefinitionId 流程定义 ID
     * @return 符合条件的流程实例信息对象列表
     */
    public static List<ProcessInstanceVo> getProcessInstancesByDefinitionId(String processDefinitionId) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();

        List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery()
                .processDefinitionId(processDefinitionId)
                .orderByProcessInstanceId()
                .desc()
                .list();

        List<ProcessInstanceVo> instanceInfos = new ArrayList<>();
        for (ProcessInstance instance : processInstances) {
            ProcessInstanceVo info = new ProcessInstanceVo(
                    instance.getId(),
                    instance.getBusinessKey(),
                    instance.getStartTime()
            );
            instanceInfos.add(info);
        }
        return instanceInfos;
    }

    /**
     * 根据流程实例 ID 查询任务列表
     *
     * @param processInstanceId 流程实例 ID
     * @return 包含待审批和已审批任务信息的列表
     */
    public static List<Map<String, Object>> getTasksByProcessInstanceId(String processInstanceId) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

        // 查询待审批任务
        TaskQuery taskQuery = processEngine.getTaskService().createTaskQuery();
        taskQuery.processInstanceId(processInstanceId);
        List<Task> pendingTasks = taskQuery.list();

        // 查询已审批任务
        HistoricTaskInstanceQuery historicTaskInstanceQuery = processEngine.getHistoryService().createHistoricTaskInstanceQuery();
        historicTaskInstanceQuery.processInstanceId(processInstanceId).finished();
        List<HistoricTaskInstance> approvedTasks = historicTaskInstanceQuery.list();

        List<Map<String, Object>> allTaskInfos = new ArrayList<>();

        // 处理待审批任务
        for (Task task : pendingTasks) {
            Map<String, Object> taskInfo = new HashMap<>();
            taskInfo.put("taskId", task.getId());
            taskInfo.put("taskName", task.getName());
            taskInfo.put("assignee", task.getAssignee());
            taskInfo.put("isHandle", false);
            allTaskInfos.add(taskInfo);
        }

        // 处理已审批任务
        for (HistoricTaskInstance historicTask : approvedTasks) {
            Map<String, Object> taskInfo = new HashMap<>();
            taskInfo.put("taskId", historicTask.getId());
            taskInfo.put("taskName", historicTask.getName());
            taskInfo.put("assignee", historicTask.getAssignee());
            taskInfo.put("isHandle", true);
            allTaskInfos.add(taskInfo);
        }

        return allTaskInfos;
    }

    /**
     * 根据任务 ID 完成审批
     *
     * @param taskId 任务 ID
     * @param isPass 是否通过审批
     */
    public static void completeTaskById(String taskId, Boolean isPass, Map<String, Object> variables) {
        // 获取默认的流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        // 获取任务服务
        TaskService taskService = processEngine.getTaskService();
        if (variables == null) {
            variables = new HashMap<>();
        }
        if (isPass) {
            variables.put(DEVICE, APPROVED);
        } else {
            variables.put(DEVICE, REJECTED);
        }
        // 完成任务并传入流程变量、决定业务走向
        taskService.complete(taskId, variables);
    }


    /**
     * 根据流程实例 ID 获取流程图
     *
     * @param processInstanceId 流程实例 ID
     * @return 流程图
     */
    public static String getDiagramByProcessInstanceId(String processInstanceId) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();

        if (processInstance != null) {
            BpmnModel model = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());

            if (model != null && model.getLocationMap().size() > 0) {
                ProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
                String activityFontName = "宋体";
                String labelFontName = "宋体";
                String annotationFontName = "宋体";
                // 生成流程图并指定字体
                InputStream inputStream = generator.generateDiagram(model, runtimeService.getActiveActivityIds(processInstanceId),
                        Collections.emptyList(), activityFontName, labelFontName, annotationFontName);
                if (inputStream != null) {
                    try {
                        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                        byte[] buffer = new byte[4096];
                        int bytesRead;
                        while ((bytesRead = inputStream.read(buffer)) != -1) {
                            outputStream.write(buffer, 0, bytesRead);
                        }
                        byte[] bytes = outputStream.toByteArray();
                        return Base64.getEncoder().encodeToString(bytes);
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            inputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return null;
    }
}


